/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.greencat.gwt.comet.client.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import com.greencat.gwt.comet.client.CometClient;
import com.greencat.gwt.comet.client.CometException;
import com.greencat.gwt.comet.client.CometSerializer;

import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.StatusCodeException;

/**
 *
 * @author p.havelaar
 */
abstract public class StreamingProtocolTransport extends BaseCometTransport {
	
	private static final String SEPARATOR = "\n";
    
	protected boolean aborted;
    protected boolean expectingDisconnection;
	protected int read;

    public void init() {
        aborted = false;
		expectingDisconnection = false;
		read = 0;
    }
        
	protected void onReceiving(int statusCode, String responseText, boolean connected) {
		if (statusCode != Response.SC_OK) {
			if (!connected) {
				expectingDisconnection = true;
				listener.onError(new StatusCodeException(statusCode, responseText), connected);
			}
		}
		else {
			int index = responseText.lastIndexOf(SEPARATOR);
			if (index > read) {
				List<Serializable> messages = new ArrayList<Serializable>();
				JsArrayString data = CometClient.split(responseText.substring(read, index), SEPARATOR);
				int length = data.length();
				for (int i = 0; i < length; i++) {
					if (aborted) {
						return;
					}
					parse(data.get(i), messages);
				}
				read = index + 1;
				if (!messages.isEmpty()) {
					listener.onMessage(messages);
				}
			}
			
			if (!connected) {
				if (expectingDisconnection) {
					listener.onDisconnected();
				}
				else {
					listener.onError(new CometException("Unexpected disconnection"), false);
				}
			}
		}
	}
	
	private void parse(String message, List<Serializable> messages) {
		if (expectingDisconnection) {
			listener.onError(new CometException("Expecting disconnection but received message: " + message), true);
		}
		else if (message.isEmpty()) {
			listener.onError(new CometException("Invalid empty message received"), true);
		}
		else {
			char c = message.charAt(0);
			switch (c) {
			case '!':
				String initParameters = message.substring(1);
				try {
                    String[] params = initParameters.split(":");
					listener.onConnected(Integer.parseInt(params[0]), Integer.parseInt(params[1]));
				}
				catch (NumberFormatException e) {
					listener.onError(new CometException("Unexpected init parameters: " + initParameters), true);
				}
				break;
			case '?':
				// clean disconnection
				expectingDisconnection = true;
				break;
			case '#':
				listener.onHeartbeat();
				break;
			case '@':
				listener.onRefresh();
				break;
			case '*':
				// ignore padding
				break;
			case '|':
				messages.add(message.substring(1));
				break;
			case ']':
				messages.add(unescape(message.substring(1)));
				break;
			case '[':
			case 'R':
			case 'r':
			case 'f':
				CometSerializer serializer = client.getSerializer();
				if (serializer == null) {
					listener.onError(new SerializationException("Can not deserialize message with no serializer: " + message), true);
				}
				else {
					try {
						messages.add(serializer.parse(message));
					}
					catch (SerializationException e) {
						listener.onError(e, true);
					}
				}
				break;
			default:
				listener.onError(new CometException("Invalid message received: " + message), true);
			}
		}
	}
	
	static String unescape(String string) {
		return string.replace("\\n", "\n").replace("\\\\", "\\");
	}
}
